/*-
* Copyright 2003-2005 Colin Percival
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#include <stdlib.h>
#include <stdio.h>
//#include <err.h>
#include "bzlib.h"
#include <io.h>
#include <fcntl.h>

#include <sys/types.h>
typedef unsigned char u_char;
typedef signed int ssize_t;

template<class T1, class T2>
void err(int i, const char* str, T1 arg1, T2 arg2) {
	char lastErrorTxt[1024];
	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, lastErrorTxt, 1024, NULL);
	printf("%s", lastErrorTxt);
	printf(str, arg1, arg2);
	exit(i);
}
template<class T>
void err(int i, const char* str, T arg) {
	char lastErrorTxt[1024];
	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, lastErrorTxt, 1024, NULL);
	printf("%s", lastErrorTxt);
	printf(str, arg);
	exit(i);
}
void err(int i, const char* str) {
	char lastErrorTxt[1024];
	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, lastErrorTxt, 1024, NULL);
	printf("%s", lastErrorTxt);
	if (str != NULL) {
		printf("%s", str);
	}
	exit(i);
}
template<class T>
void errx(int i, const char* str, T arg) {
	printf(str, arg);
	exit(i);
}
void errx(int i, const char* str) {
	printf("%s", str);
	exit(i);
}


static off_t offtin(u_char *buf)
{
	off_t y;

	y = buf[7] & 0x7F;
	y = y * 256; y += buf[6];
	y = y * 256; y += buf[5];
	y = y * 256; y += buf[4];
	y = y * 256; y += buf[3];
	y = y * 256; y += buf[2];
	y = y * 256; y += buf[1];
	y = y * 256; y += buf[0];

	if (buf[7] & 0x80) y = -y;

	return y;
}

struct bspatch_stream
{
	void* opaque;
	int(*read)(const struct bspatch_stream* stream, void* buffer, int length);
};

static int bz2_read(const struct bspatch_stream* stream, void* buffer, int length)
{
	int n;
	int bz2err;
	BZFILE* bz2;

	bz2 = (BZFILE*)stream->opaque;
	n = BZ2_bzRead(&bz2err, bz2, buffer, length);
	if (n != length)
		return -1;

	return 0;
}


int bspatch(const u_char* old, ssize_t oldsize, u_char* _new, ssize_t newsize, struct bspatch_stream* stream)
{
	u_char buf[8];
	ssize_t oldpos, newpos;
	ssize_t ctrl[3];
	ssize_t i;

	oldpos = 0; newpos = 0;
	while (newpos<newsize) {
		/* Read control data */
		for (i = 0; i <= 2; i++) {
			if (stream->read(stream, buf, 8))
				return -1;
			ctrl[i] = offtin(buf);
		};

		/* Sanity-check */
		if (newpos + ctrl[0]>newsize)
			return -1;

		/* Read diff string */
		if (stream->read(stream, _new + newpos, ctrl[0]))
			return -1;

		/* Add old data to diff string */
		for (i = 0; i<ctrl[0]; i++)
			if ((oldpos + i >= 0) && (oldpos + i<oldsize))
				_new[newpos + i] += old[oldpos + i];

		/* Adjust pointers */
		newpos += ctrl[0];
		oldpos += ctrl[0];

		/* Sanity-check */
		if (newpos + ctrl[1]>newsize)
			return -1;

		/* Read extra string */
		if (stream->read(stream, _new + newpos, ctrl[1]))
			return -1;

		/* Adjust pointers */
		newpos += ctrl[1];
		oldpos += ctrl[2];
	};

	return 0;
}

int main(int argc, char * argv[])
{
	FILE * f, *cpf, *dpf, *epf;
	BZFILE * cpfbz2, *dpfbz2, *epfbz2;
	BZFILE* bz2;
	int cbz2err, dbz2err, ebz2err;
	int fd;
	ssize_t oldsize, newsize;
	u_char header[32];// , buf[8];
	u_char *old, *_new;
	//off_t oldpos, newpos;
	//off_t ctrl[3];
	//off_t lenread;
	off_t i;
	int bz2err;
	struct bspatch_stream stream;

	if (argc != 4) errx(1, "usage: %s oldfile newfile patchfile\n", argv[0]);

	/* Open patch file */
	if ((f = fopen(argv[3], "rb")) == NULL)
		err(1, "fopen(%s)", argv[3]);

	/*
	File format:
	0	8	"BSDIFF40"
	8	8	X
	16	8	Y
	24	8	sizeof(newfile)
	32	X	bzip2(control block)
	32+X	Y	bzip2(diff block)
	32+X+Y	???	bzip2(extra block)
	with control block a set of triples (x,y,z) meaning "add x bytes
	from oldfile to x bytes from the diff block; copy y bytes from the
	extra block; seek forwards in oldfile by z bytes".
	*/

	/* Read header */
	if (fread(header, 1, 24, f) < 24) {
		if (feof(f))
			errx(1, "Corrupt patch\n");
		err(1, "fread(%s)", argv[3]);
	}

	/* Check for appropriate magic */
	if (memcmp(header, "ENDSLEY/BSDIFF43", 16) != 0)
		errx(1, "Corrupt patch\n");

	/* Read lengths from header */
	newsize = offtin(header + 16);
	if (newsize<0)
		errx(1, "Corrupt patch\n");

	/* Close patch file and re-open it via libbzip2 at the right places */
	if (fclose(f))
		err(1, "fclose(%s)", argv[3]);
	if ((cpf = fopen(argv[3], "rb")) == NULL)
		err(1, "fopen(%s)", argv[3]);
	if (fseek(cpf, 24, SEEK_SET))
		err(1, "fseeko(%s, %d)", argv[3], 24);
	if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)
		errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
	if ((dpf = fopen(argv[3], "rb")) == NULL)
		err(1, "fopen(%s)", argv[3]);
	if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
		errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
	if ((epf = fopen(argv[3], "rb")) == NULL)
		err(1, "fopen(%s)", argv[3]);
	if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
		errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);

	//org:
	//if(((fd=open(argv[1],O_RDONLY,0))<0) ||
	//	((oldsize=lseek(fd,0,SEEK_END))==-1) ||
	//	((old=malloc(oldsize+1))==NULL) ||
	//	(lseek(fd,0,SEEK_SET)!=0) ||
	//	(read(fd,old,oldsize)!=oldsize) ||
	//	(close(fd)==-1)) err(1,"%s",argv[1]);
	//new:
	//Read in chunks, don't rely on read always returns full data!
	if (((fd = open(argv[1], O_RDONLY | O_BINARY | O_NOINHERIT, 0))<0) ||
		((oldsize = lseek(fd, 0, SEEK_END)) == -1) ||
		((old = (u_char*)malloc(oldsize + 1)) == NULL) ||
		(lseek(fd, 0, SEEK_SET) != 0))
		err(1, "%s", argv[1]);
	int r = oldsize;
	while (r>0 && (i = read(fd, old + oldsize - r, r))>0) r -= i;
	if (r>0 || close(fd) == -1) err(1, "%s", argv[1]);

	if ((_new = (u_char*)malloc(newsize + 1)) == NULL) err(1, NULL);

	if(NULL == (bz2 = BZ2_bzReadOpen(&bz2err, f, 0, 0, NULL, 0)))
		errx(1, "BZ2_bzReadOpen, bz2err=%d", bz2err);

	stream.read = bz2_read;
	stream.opaque = bz2;
	if (bspatch(old, oldsize, _new, newsize, &stream))
		errx(1, "bspatch");

	/* Clean up the bzip2 reads */
	BZ2_bzReadClose(&bz2err, bz2);
	fclose(f);

	/* Write the new file */
	if (((fd = open(argv[2], O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666))<0) ||
		(write(fd, _new, newsize) != newsize) || (close(fd) == -1))
		err(1, "%s", argv[2]);

	free(_new);
	free(old);

	return 0;
}
